/*
 * c_hash.h
 *
 *  Created on: 27 Apr 2010
 *      Author: osky
 */


#include "c_hash.h"
#include "../CapaFisica/Archivo.h"



/*** SECCION PRIVADA ***/
/*
unsigned int c_hash::f_hash (unsigned int clave)
     {
     unsigned int resultado=clave;
     return resultado;
     }
*/

/*** SECCION PUBLICA ***/


/* ABRE UN ARCHIVO INDEXADO */

c_hash::c_hash ( const char* nombre_fisico )
     {

	int res;

    	     Archivo::setNombre(nombre_fisico);
    	     Archivo::setTamanioBloque(LARGO_BLOQUE);
    	     Archivo::setCantidadMaximaBloques(8000);
         /* Intenta abrirlo */
         
         if(!(Archivo::getInstance("r+")))/* sino puede lo crea */
               {
        	                                                                            	   
                   if(!(Archivo::getInstance("w+"))) { res=RES_NOK; }
                   
                   else{
	          #ifdef _DEBUG_
                   printf("El archivo no existia lo crea\n");
                  #endif 

                   tabla_hash.set_pos (0,tabla_hash.total_reg(),0 );
                   tabla_hash.save();
                    
                   c_bucket bucket = c_bucket(0);
                   bucket.save();
                   
                   primer_bucket_libre = 1;
                   
                   }
               }

         else
         {

	 #ifdef _DEBUG_ 
       	 printf("El archivo existia lo abre normalmente\n");
         #endif        	 
        
         /* Calcula el primer bucket_libre */
         
         
         primer_bucket_libre  = Archivo::getInstance()->getUltimoBloque() + 1;
         primer_bucket_libre -= BLOQUES_TABLA ; 

         tabla_hash.load();

         }


         #ifdef _DEBUG_  
         printf("---> OPEN Ultimo bloque escrito: %d\n",Archivo::getInstance()->getUltimoBloque());
         printf ("---> OPEN: Proximo bucket libre: %d (Bloques de tabla %d) \n", primer_bucket_libre, BLOQUES_TABLA);
         #endif	
	 res=RES_OK;


     }


c_hash::~c_hash()
{

     tabla_hash.save();
     Archivo::deleteInstance();


}



     /******************************************/
     /* INSERTA UN REGISTRO DENTRO DEL ARCHIVO */
     /******************************************/

int c_hash::insertar_dato ( c_dato *dato)
     {

     unsigned int nro_bucket;
     unsigned int clave=dato->algoritmo_para_hash ();
     unsigned int resultado;

     nro_bucket=tabla_hash.obtener_nro_bucket(clave);
          #ifdef _DEBUG_	
     printf ("c_hash.insertar_dato()  clave: %d -> bucket: %d \n" ,clave, nro_bucket);
          #endif		

     c_bucket bucket = c_bucket(nro_bucket);
     bucket.load();
     c_registro reg( clave ,dato->largo () , dato->serializar() );

     /*********************************************************************/


         if (bucket.buscar_clave (clave) < 0 )
         {

         /* Intenta insertarlo en el bucket */
             if ( bucket.insertar_reg(&reg) < 0 )
	        { /* Sino entra */

                 /* Divide el bucket */
    	          c_bucket bucket_0 = c_bucket (nro_bucket);
    		  c_bucket bucket_1 = c_bucket (primer_bucket_libre);

                  int bit_split =  bucket.dividir ( &bucket_0 , &bucket_1 ) ;
                  if (bit_split == RES_SUPERA_MAX_DIV) {
                				        printf ("No se puede ingresar el registro, superada la capacidad del archivo\n");
                		    		        resultado=-2;
                		    		       }
                  else
		        { 
		          #ifdef _DEBUG_	
		          printf ("El bucket %d fue dividido a nivel del bit %d\n",nro_bucket,bit_split);
        	          #endif		

        	          bucket_0.save();
	        	  bucket_1.save();
	        	  tabla_hash.actualizar_por_split ( clave, primer_bucket_libre , bit_split);
	        	  primer_bucket_libre++;  /* HACE FALTA? */
	        	  resultado=insertar_dato (dato); /* RECURSIVO */
	        	 }

    		  }
              else {
                    bucket.save();    /* SI ENTRA GRABA EL BUCKET */
                    resultado=1;
                   }

         } else { printf ("Clave:%d (%d) YA EXISTENTE!\n", invertir_bits(reg.get_clave()),reg.get_clave());
                  resultado=-1;
                }



     return resultado;
     }



     /******************************************/
     /*  BORRA UN REGISTRO DENTRO DEL ARCHIVO */
     /******************************************/  

int c_hash::buscar_dato ( c_dato *dato)
     {

     int nro_reg;
     unsigned int nro_bucket;
     unsigned int clave=dato->algoritmo_para_hash ();
     unsigned int resultado;

     nro_bucket=tabla_hash.obtener_nro_bucket(clave);
     #ifdef _DEBUG_
     printf ("c_hash.buscar_dato()  clave: %d -> bucket: %d \n" ,clave, nro_bucket);
     #endif

     c_bucket bucket = c_bucket(nro_bucket);
     bucket.load();

     /*********************************************************************/
 

         if ( (nro_reg=bucket.buscar_clave (clave))>=0 )
         {

		    printf("Clave: %d (%d) encontrada en bucket: %d registro: %d\n", invertir_bits(clave), clave,nro_bucket,nro_reg) ;		

                    bucket.dump_registro(nro_reg);

         } else { printf ("Clave: %d (%d) NO EXISTENTE!\n",invertir_bits(clave), clave);
                  resultado=RES_NOK;
                }


     return resultado;
     }



     /******************************************/
     /*  BORRA UN REGISTRO DENTRO DEL ARCHIVO */
     /******************************************/

int c_hash::eliminar_dato ( c_dato *dato)
     {


     unsigned int nro_bucket;
     unsigned int clave=dato->algoritmo_para_hash ();
     unsigned int resultado;

     nro_bucket=tabla_hash.obtener_nro_bucket(clave);
     #ifdef _DEBUG_
     printf ("c_hash.eliminar_dato()  clave: %d -> bucket: %d \n" ,clave, nro_bucket);
     #endif

     c_bucket bucket = c_bucket(nro_bucket);
     bucket.load();

     /*********************************************************************/


         if (bucket.eliminar_dato (clave)>0)
         {

                    bucket.save();
                    resultado=RES_OK;
                    printf ("Eliminando clave: %d (%d): Eliminado!\n",invertir_bits(clave), clave);


         } else { printf ("Eliminando clave: %d (%d): Clave No Existente!\n",invertir_bits(clave), clave);
                  resultado=RES_NOK;
                }


     return resultado;
     }



int c_hash::modificar_dato ( c_dato *dato)
     {


     unsigned int nro_bucket;
     unsigned int clave=dato->algoritmo_para_hash ();
     unsigned int resultado;

     nro_bucket=tabla_hash.obtener_nro_bucket(clave);
     #ifdef _DEBUG_
     printf ("c_hash.eliminar_dato()  clave: %d -> bucket: %d \n" ,clave, nro_bucket);
     #endif

     c_bucket bucket = c_bucket(nro_bucket);
     bucket.load();

     /*********************************************************************/


         if (bucket.eliminar_dato (clave)>0)
         {

                    bucket.save();
                                        
                    printf ("Modificando clave: %d (%d)\n",invertir_bits(clave), clave); 
                    
                    resultado = insertar_dato(dato);
                    

         } else { printf ("Modificando clave: %d (%d): Clave No Existente!\n",invertir_bits(clave), clave);
                  resultado=RES_NOK;
                }



     return resultado;
     }




void c_hash::dump(FILE *handler_txt)
     {

         fprintf (handler_txt,"Dump de la tabla...\n");
         tabla_hash.dump(handler_txt);
         
         fprintf (handler_txt,"Dump de buckets...\n");
         for ( unsigned int i=0; i<primer_bucket_libre; i++)
        	{
        	  c_bucket bucket(i);
        	  bucket.load();
        	  bucket.dump(handler_txt);

        	}


     }


